#include <iostream>
#define MAXN 300
using namespace std;

int parent[MAXN * 3], ranks[MAXN * 3];

void init(int n) {
    for (int i = 0; i < n; i++)
        parent[i] = i, ranks[i] = 0;
}

int find(int x) {
    if (parent[x] == x) return x;
    return parent[x] = find(parent[x]);
}

void unite(int x, int y) {
    x = find(x); y = find(y);
    if (x == y) return;
    if (ranks[x] < ranks[y]) {
        parent[x] = y;
    } else {
        parent[y] = x;
        if (ranks[x] == ranks[y]) ranks[y]++;
    }
}

bool same(int x, int y) {
    return find(x) == find(y);
}

int N, K;
void solve();

int main() {
    cin >> N >> K;
    solve();
}

// 由于对x和y的种类无法得知
// 因此可以首先假设它们同时属于这些种类
// x -> A; x + N -> B; x + 2N -> C
// 如果X和Y属于同一个种类, 那么X和B同时属于A, B, C;
// 如果X->B, 那么这种捕捉关系为真
// 因此在这里并查集的主要作用在于判断X属于某一类和Y属于某一类是否同时为真的集合
void solve() {
    int ans = 0, kind = 0, x, y;
    while (K--) {
        cin >> kind >> x >> y;
        if (x <= 0 || x > N || y <= 0 || y > N) { ans++; continue; }

        if (kind == 1) {
            if (same(x, y + N) || same(x, y + 2 * N)) ans++;
            else {
                unite(x, y);
                unite(x + N, y + N);
                unite(x + 2 * N, y + 2 * N);
            }
        } else {
            if (same(x, y) || same(x, y + 2 * N)) ans++;
            else {
                unite(x, y + N);
                unite(x + N, y + 2 * N);
                unite(x + 2 * N, y);
            }
        }
    }

    cout << ans << endl;
}
